﻿// essentially a direct port of VB
// todo: make it neat

using Gargoyle.Roton;
using Gargoyle.ZZT;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Lyon
{
    public partial class WinFormGDI : Form
    {
        private Audio Audio;
        private string CurrentName;
        private Encoding Encoding = Encoding.GetEncoding(437);
        private BufferInput Input;
        private bool SuperSpeed = false;
        private EventTimer Timer;
        private TextModeVideo Video;
        private bool VideoBlink;
        private int VideoTick;
        private System.Timers.Timer VideoTimer;
        private Game ZZT;

        private bool AudioEnabled = true;

        private bool VideoBlinkOn = true;
        private string VideoFont = null;
        private string VideoPalette = null;
        private decimal VideoScale = 1;
        private bool VideoSmoothing = false;

        public WinFormGDI()
        {
            InitializeComponent();
            DoubleClick += GameFormDoubleClick;
            KeyDown += GameFormKeyDown;
            Shown += GameFormShown;
            
            /*
             * Video Settings
             */
            VideoBlinkOn = Convert.ToBoolean(Convert.ToInt32(ConfigManager.GetValue("Video", "Blink", "1")));
            VideoFont = ConfigManager.GetValue("Video", "Font");
            VideoPalette = ConfigManager.GetValue("Video", "Palette");
            VideoScale = Convert.ToDecimal(ConfigManager.GetValue("Video", "Scale", "1"));
            VideoSmoothing = Convert.ToBoolean(Convert.ToInt32(ConfigManager.GetValue("Video", "Smoothing", "0")));

            /*
             * Audio Settings
             */
            AudioEnabled = Convert.ToBoolean(Convert.ToInt32(ConfigManager.GetValue("Audio", "Enabled", "1")));
        }

        private void GameFormDoubleClick(object sender, EventArgs e)
        {
            string filename = Common.ShowSaveScreenshotDialog();
            if(filename != null)
                BackgroundImage.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
        }

        private void GameFormKeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.I:
                    ZZT.ToggleFlag("I");
                    break;
                case Keys.D1:
                    if(e.Control)
                        this.ClientSize = new Size(Video.Bitmap.Width, Video.Bitmap.Height);
                    break;
                case Keys.D2:
                    if(e.Control)
                        this.ClientSize = new Size(Video.Bitmap.Width * 2, Video.Bitmap.Height * 2);
                    break;
                case Keys.Oemtilde:
                    if(e.Control)
                        VideoSmoothing = !VideoSmoothing;
                    break;
                default:
                    break;
            }
            Input.KeyDown(e.KeyData);
        }

        private void InitTimer()
        {
            if (Timer != null)
            {
                Timer.Stop();
                Timer = null;
            }
            if (SuperSpeed)
                Timer = new EventTimer(Tick, double.MaxValue, int.MaxValue);
            else
                Timer = new EventTimer(Tick, 71.2d, 30);
        }

        protected override void OnClosed(EventArgs e)
        {
            if (Timer != null)
                Timer.Dispose();
            base.OnClosed(e);
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            if(VideoSmoothing)
                e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
            else
                e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

            e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
            base.OnPaintBackground(e);
        }

        [STAThread()]
        private void Tick(object state, EventArgs e)
        {
            if (ZZT != null && ZZT.Running && !ZZT.Busy)
            {
                if (Debugger.IsAttached)
                    ZZT.Execute();
                else
                {
                    try
                    {
                        ZZT.Execute();
                    }
                    catch (Exception ex)
                    {
                        Timer.Stop();
                        Common.ErrorMessage = ex.Message;
                        Common.ErrorStackTrace = ex.StackTrace;
                        Thread ErrorThread = new Thread(new ParameterizedThreadStart(Common.DisplayError));
                        ErrorThread.SetApartmentState(ApartmentState.STA);
                        ErrorThread.Start();
                        ZZT = null;
                    }
                }
            }
        }

        private void UpdateScreen(object sender, System.Timers.ElapsedEventArgs e)
        {
            string GameName;
            bool PerformUpdate = false;

            if (ZZT == null || !ZZT.Running)
            {
                Close();
                return;
            }

            GameName = ZZT.Name();
            if (GameName != CurrentName)
            {
                CurrentName = GameName;
                Text = "Lyon - [" + GameName + "]";
            }

            VideoTick++;
            if ((VideoTick % 15) == 0)
            {
                VideoBlink = !VideoBlink;
                Video.SetBlink(VideoBlink);
                VideoTick = 0;
                PerformUpdate = true;
            }

            PerformUpdate |= ZZT.UpdateScreen;

            if (PerformUpdate)
                Invalidate();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
        }

        private void GameFormShown(object sender, EventArgs e)
        {
            Cache InitCache = Common.LoadWorld();

            if (InitCache == null)
                Close();
            else
            {
                int VideoWidth = 80;
                if (Common.WideMode)
                    VideoWidth /= 2;

                Video = new TextModeVideo(new Size(VideoWidth, 25), null, null, Common.WideMode);

                if(VideoFont != "")
                {
                    FontData font = FontLoader.GetFontData(VideoFont);
                    Video.SetFont(font.Usable ? font.FontBitmap : null);
                }

                if(VideoPalette != "")
                {
                    PaletteData palette = PaletteLoader.GetPaletteData(VideoPalette);
                    Video.SetPalette(palette.Usable ? palette.Palette : null);
                }

                if(VideoBlinkOn)
                    Video.SetBright(false);
                else
                    Video.SetBright(true);

                Input = new BufferInput();

                if(AudioEnabled)
                    Audio = new AudioOpenAL(44100, Gargoyle.Roton.Audio.BitDepth.Signed16Bit, 2048);
                else
                    Audio = new Audio();

                ZZT = new Game(Video, Audio, Input, InitCache, Common.EngineID, true);
                ClientSize = new Size((int)(Video.Bitmap.Width * VideoScale), (int)(Video.Bitmap.Height * VideoScale));
                BackgroundImage = Video.Bitmap;
                InitTimer();
                VideoTimer = new System.Timers.Timer(1);
                VideoTimer.SynchronizingObject = this;
                ZZT.Load(Common.GameFile);
                Timer.Start();
                VideoTimer.Start();
                VideoTimer.Elapsed += UpdateScreen;

                Video.BitmapInvalidated += Video_BitmapInvalidated;

                MemoryViewer memViewer = new MemoryViewer();
                memViewer.Memory = ZZT.Memory;
                memViewer.Show();
            }
        }

        private void Video_BitmapInvalidated(object sender, EventArgs e)
        {
            BackgroundImage = Video.Bitmap;
        }
    }
}
